/// Includes projects
#include "svmTrainData.h"
/// Includes cuda
#include <cutil_math.h>
/// Includes system
#include <cctype>

const float POS = 1.0f;
const float NEG = -1.0f;

void svmTrainData::cleanData() {
	//free(data);
	free(transposeData);
	free(labels);
	free(ellData);
	free(ellRowLen);
	if(oaLabels!=NULL) free(oaLabels);
}

void svmTrainData::readSvm(char *trainingFilename) {
	//printf("--------------------[Loading Training File]--------------------\n");
	FILE* inputFilePointer = fopen(trainingFilename, "r");
	if (inputFilePointer == NULL) {
		printf("!!!!!SVM file not found:%s\n",trainingFilename);// system("PAUSE");
		exit(1);
	}

	line = (char*)malloc(max_line_len);
	
	//Scan the input data to get the dimension and # of training points.
	while(readline(inputFilePointer)!=NULL) {
		int noOfEntry = 0;
		char *p = strtok(line," \t");
		while(1)
		{
			p = strtok(NULL," \t");
			if(p == NULL || *p == '\n')
				break;
			int tempDim = atoi(p); //addition char':' will be ignored
			if(tempDim > nDimension) {
				nDimension = tempDim;
			}
			noOfEntry++;
		}
		if(ellWidth < noOfEntry)
			ellWidth = noOfEntry;
		nPoints++;
	}

	// Rewind and prepare for next scan.
	rewind(inputFilePointer); 

	printf("\n> Input data found: %d points, %d dimension\n", nPoints, nDimension);

	//According to the information of dimension and # of training points, allocate host memory to store the data.
	//data = (float*)malloc(sizeof(float)*nPoints*nDimension);
	//memset(data,0,nPoints*nDimension*sizeof(float));
	labels = (float*)malloc(sizeof(float)*nPoints);

	transposeData = (float*)malloc(sizeof(float)*nPoints*nDimension);
	memset(transposeData,0,sizeof(float)*nPoints*nDimension);

	printf("> Allocating ELLPACK sparse matrix, with height of %d x nPoints\n",ellWidth);
	ellData = (int2 *)malloc(sizeof(int2)*nPoints*ellWidth);
	memset(ellData,0,sizeof(int2)*nPoints*ellWidth);
	ellRowLen = (int *)malloc(sizeof(int)*nPoints);
	
	char * endPtr;
	for(int i=0;i<nPoints;i++) {
		int instMaxIndex = -1;
		readline(inputFilePointer);
		char * cLabel = strtok(line," \t");
		labels[i] = (float)strtod(cLabel,&endPtr);
		if(endPtr == cLabel)
			exitInputError(i+1);
		int ellTempLen = 0;
		while(1) {
			char * cIdx = strtok(NULL,":");
			char * cVal = strtok(NULL," \t");

			if(cVal == NULL) break;

			errno = 0;
			int idx = (int)strtol(cIdx,&endPtr,10);
			if(endPtr == cIdx || errno !=0 || *endPtr != '\0' || idx <= instMaxIndex)
				exitInputError(i+1);
			else
				instMaxIndex = idx;

			errno = 0;
			float val = (float)strtod(cVal,&endPtr);
			if(endPtr == cVal || errno !=0 || (*endPtr != '\0' && !isspace(*endPtr)))
				exitInputError(i+1);
			if(val != 0.0f) {
				//data[(idx-1)*nPoints+i] = val;
				transposeData[i*nDimension+idx-1] = val;
				ellData[i+ellTempLen*nPoints] = make_int2(idx-1,*reinterpret_cast<int*>(&val));
				//printf("%d:%g ",ellData[i+ellTempLen*nPoints].x, *(float*)&ellData[i+ellTempLen*nPoints].y);
				ellTempLen++;
			}
		}
		ellRowLen[i] = ellTempLen;
	}
	free(line);
	fclose(inputFilePointer);
	return;
}

void svmTrainData::genOOCode() {
	/// find out all classes
	findClasses();
	nTasks = (nClass*nClass-nClass)/2;

	/// re-permutation data
	groupClasses();
}

void svmTrainData::genOACode() {
	/// find out all classes
	findClasses();
	nTasks = (nClass == 2)?(1):(nClass);//groupClasses();

	/// generate one-against-all labels
	oaLabels = (float*)malloc(nPoints*nTasks*sizeof(float));
	classIt = classes.begin();
	for(int i=0;i<nTasks;i++,classIt++) {
		for(int j=0;j<nPoints;j++) {
			int thisLabel = (int)labels[j];
			if(thisLabel == classIt->first)
				oaLabels[i*nPoints+j] = POS;
			else
				oaLabels[i*nPoints+j] = NEG;
		}
	}
}

void svmTrainData::groupClasses() {
	int *start = (int*)malloc(nClass*sizeof(int));
	int *perm = (int*)malloc(nPoints*sizeof(int));
	start[0] = 0;
	classIt = classes.begin();
	for(int i=1;i<nClass;i++,classIt++) {
		start[i] = start[i-1] + classIt->second;
	}
	for(int i=0;i<nPoints;i++) {
		int thisLabel = (int)labels[i];
		classIt = classes.begin();
		for(int j=0; j<nClass;classIt++,j++) {
			if(thisLabel == classIt->first) {
				perm[start[j]] =i;
				start[j]++;
				break;
			}	
		}
	}
	start[0] = 0;
	classIt = classes.begin();
	for(int i=1;i<nClass;i++,classIt++)
		start[i] = start[i-1] + classIt->second;
	float* newTransposeData = (float*)malloc(sizeof(float)*nPoints*nDimension);
	int2* newEllData = (int2 *)malloc(sizeof(int2)*nPoints*ellWidth);
	memset(newEllData,0,sizeof(int2)*nPoints*ellWidth);
	int* newEllRowLen = (int *)malloc(sizeof(int)*nPoints);
	float* newLabels = (float *)malloc(sizeof(float)*nPoints);

	for(int i=0;i<nPoints;i++) {
		int newIndex = perm[i];
		for(int j=0;j<nDimension;j++) {
			newTransposeData[i*nDimension+j] = transposeData[newIndex*nDimension+j];
		}
		newEllRowLen[i] = ellRowLen[newIndex];
		for(int j=0;j<newEllRowLen[i];j++) {
			newEllData[i+nPoints*j] = ellData[newIndex+nPoints*j];
			//printf("%d:%g ",(*(newEllData+i+nPoints*j)).x,*(float*)&(*(newEllData+i+nPoints*j)).y);
			//printf("%d %d -> %d %d\n",ellData[newIndex+nPoints*j].x, ellData[newIndex+nPoints*j].y, newEllData[i+nPoints*j].x ,newEllData[i+nPoints*j].y);
		}
		newLabels[i] = labels[newIndex];
	}

	free(transposeData);
	free(ellData);
	free(ellRowLen);
	free(labels);
	free(start);
	free(perm);
	transposeData = newTransposeData;
	ellData = newEllData;
	ellRowLen = newEllRowLen;
	labels = newLabels;
}

void svmTrainData::findClasses() {
	for(int i=0;i<nPoints;i++) {
		int thisLabel = (int)labels[i];
		classes[thisLabel]++;
	}
	nClass = classes.size();
	printf("\n> find classes:%d\n",nClass);
	for(classIt = classes.begin(); classIt!= classes.end(); classIt++) {
		printf(" [%d :%d]\n",classIt->first, classIt->second);
	}
	printf("\n");
}